package com.yh.mybatis.extention.plugin.mapper;

import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.xml.Attribute;
import org.mybatis.generator.api.dom.xml.TextElement;
import org.mybatis.generator.api.dom.xml.XmlElement;
import org.mybatis.generator.codegen.mybatis3.ListUtilities;
import org.mybatis.generator.codegen.mybatis3.MyBatis3FormattingUtilities;
import org.mybatis.generator.codegen.mybatis3.xmlmapper.elements.AbstractXmlElementGenerator;
import org.mybatis.generator.config.GeneratedKey;

import java.util.List;

import static org.mybatis.generator.internal.util.StringUtility.stringHasValue;

/**
 * @author liubing
 */
public class YhXmlElementGenerator extends AbstractXmlElementGenerator {
    /**
     * 索引列
     */
    private List<String> columnIndexs ;

    public YhXmlElementGenerator(List<String> columnIndexs){
        this.columnIndexs =columnIndexs ;
    }

    @Override
    public void addElements(XmlElement parentElement) {

        //增加方法
        addInsertElements(parentElement);
        //删除方法
        addDeleteByIdElements(parentElement);
        //更新方法
        addUpdateByIdElements(parentElement);
        //批量查询方法
        addGetListByCriteriaElements(parentElement);
        //分页查询条件参数
        addPageWhereElements(parentElement);
        //查询总数
        addSelectCountElements(parentElement);
        //分页查询
        addPageListElements(parentElement);
    }

    /**
     * 批量查询方法
     * @param parentElement
     */
    private void addGetListByCriteriaElements(XmlElement parentElement) {
        // 增加base_query
        XmlElement baseQuerySQL = new XmlElement("sql");

        baseQuerySQL.addAttribute(new Attribute("id", "select_where_clause"));
        //在这里添加where条件
        //设置trim标签
        XmlElement selectTrimElement = new XmlElement("trim");
        selectTrimElement.addAttribute(new Attribute("prefix", "WHERE"));
        //添加where和and
        selectTrimElement.addAttribute(new Attribute("prefixOverrides", "AND | OR"));
        StringBuilder sb = new StringBuilder();

        for(IntrospectedColumn introspectedColumn : introspectedTable.getAllColumns()) {
            //$NON-NLS-1$
            String columnName = MyBatis3FormattingUtilities.getEscapedColumnName(introspectedColumn);
            if(columnIndexs == null || !columnIndexs.contains(columnName)){
                continue;
            }
            XmlElement selectNotNullElement = new XmlElement("if");
            sb.setLength(0);
            sb.append("null != ");
            sb.append(introspectedColumn.getJavaProperty());
            selectNotNullElement.addAttribute(new Attribute("test", sb.toString()));
            sb.setLength(0);
            // 添加and
            sb.append(" and ");
            sb.append(MyBatis3FormattingUtilities.getEscapedColumnName(introspectedColumn));
            // 添加等号
            sb.append(" = ");
            sb.append(MyBatis3FormattingUtilities.getParameterClause(introspectedColumn));
            selectNotNullElement.addElement(new TextElement(sb.toString()));
            selectTrimElement.addElement(selectNotNullElement);
        }
        baseQuerySQL.addElement(selectTrimElement);
        parentElement.addElement(baseQuerySQL);

        // 公用select
        sb.setLength(0);
        sb.append("select ");
        sb.append("\r\n");
        sb.append("   <include refid=\"Base_Column_List\" /> ");
        sb.append("\r\n");
        sb.append("   from ");
        sb.append(introspectedTable.getFullyQualifiedTableNameAtRuntime());
        TextElement selectText = new TextElement(sb.toString());

        // 公用include
        XmlElement include = new XmlElement("include");
        include.addAttribute(new Attribute("refid", "select_where_clause"));


        // 增加list
        XmlElement list = new XmlElement("select");
        list.addAttribute(new Attribute("id", "getListByCriteria"));
        list.addAttribute(new Attribute("resultMap", "BaseResultMap"));
        list.addAttribute(new Attribute("parameterType", introspectedTable.getBaseRecordType()));
        list.addElement(selectText);
        list.addElement(include);
        sb.setLength(0);
        sb.append(" order by id desc ");
        TextElement orderByText = new TextElement(sb.toString());
        list.addElement(orderByText);
        parentElement.addElement(list);
    }

    /**
     * 增加方法
     * @param parentElement
     */
    private void addInsertElements(XmlElement parentElement) {
        XmlElement answer = new XmlElement("insert");
        answer.addAttribute(new Attribute(
                "id", introspectedTable.getInsertStatementId()));
        FullyQualifiedJavaType parameterType = introspectedTable.getRules()
                .calculateAllFieldsClass();
        answer.addAttribute(new Attribute("parameterType",
                parameterType.getFullyQualifiedName()));
        context.getCommentGenerator().addComment(answer);
        GeneratedKey gk = introspectedTable.getGeneratedKey();
        if (gk != null) {
            IntrospectedColumn introspectedColumn = introspectedTable
                    .getColumn(gk.getColumn());
            // if the column is null, then it's a configuration error. The
            // warning has already been reported
            if (introspectedColumn != null) {
                if (gk.isJdbcStandard()) {
                    answer.addAttribute(new Attribute("useGeneratedKeys", "true"));
                    answer.addAttribute(new Attribute("keyProperty", introspectedColumn.getJavaProperty()));
                    answer.addAttribute(new Attribute("keyColumn", introspectedColumn.getActualColumnName()));
                } else {
                    answer.addElement(getSelectKey(introspectedColumn, gk));
                }
            }
        }

        StringBuilder sb = new StringBuilder();

        sb.append("insert into ");
        sb.append(introspectedTable.getFullyQualifiedTableNameAtRuntime());
        answer.addElement(new TextElement(sb.toString()));

        XmlElement insertTrimElement = new XmlElement("trim");
        insertTrimElement.addAttribute(new Attribute("prefix", "("));
        insertTrimElement.addAttribute(new Attribute("suffix", ")"));
        insertTrimElement.addAttribute(new Attribute("suffixOverrides", ","));
        answer.addElement(insertTrimElement);

        XmlElement valuesTrimElement = new XmlElement("trim");
        valuesTrimElement.addAttribute(new Attribute("prefix", "values ("));
        valuesTrimElement.addAttribute(new Attribute("suffix", ")"));
        valuesTrimElement.addAttribute(new Attribute("suffixOverrides", ","));
        answer.addElement(valuesTrimElement);

        for (IntrospectedColumn introspectedColumn : ListUtilities.removeIdentityAndGeneratedAlwaysColumns(introspectedTable
                .getAllColumns())) {

            if (introspectedColumn.isSequenceColumn()
                    || introspectedColumn.getFullyQualifiedJavaType().isPrimitive()) {
                sb.setLength(0);
                sb.append(MyBatis3FormattingUtilities
                        .getEscapedColumnName(introspectedColumn));
                sb.append(',');
                insertTrimElement.addElement(new TextElement(sb.toString()));

                sb.setLength(0);
                sb.append(MyBatis3FormattingUtilities
                        .getParameterClause(introspectedColumn));
                sb.append(',');
                valuesTrimElement.addElement(new TextElement(sb.toString()));

                continue;
            }

            XmlElement insertNotNullElement = new XmlElement("if");
            sb.setLength(0);
            sb.append(introspectedColumn.getJavaProperty());
            sb.append(" != null");
            insertNotNullElement.addAttribute(new Attribute(
                    "test", sb.toString()));

            sb.setLength(0);
            sb.append(MyBatis3FormattingUtilities
                    .getEscapedColumnName(introspectedColumn));
            sb.append(',');
            insertNotNullElement.addElement(new TextElement(sb.toString()));
            insertTrimElement.addElement(insertNotNullElement);

            XmlElement valuesNotNullElement = new XmlElement("if");
            sb.setLength(0);
            sb.append(introspectedColumn.getJavaProperty());
            sb.append(" != null");
            valuesNotNullElement.addAttribute(new Attribute(
                    "test", sb.toString()));

            sb.setLength(0);
            sb.append(MyBatis3FormattingUtilities
                    .getParameterClause(introspectedColumn));
            sb.append(',');
            valuesNotNullElement.addElement(new TextElement(sb.toString()));
            valuesTrimElement.addElement(valuesNotNullElement);
        }

        if (context.getPlugins().sqlMapInsertSelectiveElementGenerated(
                answer, introspectedTable)) {
            parentElement.addElement(answer);
        }
    }

    /**
     * 删除方法
     * @param parentElement
     */
    private void addDeleteByIdElements(XmlElement parentElement) {
        XmlElement answer = new XmlElement("delete");

        answer.addAttribute(new Attribute("id", "deleteById"));
        String parameterClass;
        if (introspectedTable.getRules().generatePrimaryKeyClass()) {
            parameterClass = introspectedTable.getPrimaryKeyType();
        } else {
            // PK fields are in the base class. If more than on PK
            // field, then they are coming in a map.
            if (introspectedTable.getPrimaryKeyColumns().size() > 1) {
                parameterClass = "map";
            } else {
                parameterClass = introspectedTable.getPrimaryKeyColumns()
                        .get(0).getFullyQualifiedJavaType().toString();
            }
        }
        answer.addAttribute(new Attribute("parameterType",
                parameterClass));

        context.getCommentGenerator().addComment(answer);

        StringBuilder sb = new StringBuilder();
        sb.append("delete from ");
        sb.append(introspectedTable.getFullyQualifiedTableNameAtRuntime());
        answer.addElement(new TextElement(sb.toString()));

        boolean and = false;
        for (IntrospectedColumn introspectedColumn : introspectedTable
                .getPrimaryKeyColumns()) {
            sb.setLength(0);
            if (and) {
                sb.append("  and ");
            } else {
                sb.append("where ");
                and = true;
            }

            sb.append(MyBatis3FormattingUtilities
                    .getEscapedColumnName(introspectedColumn));
            sb.append(" = ");
            sb.append(MyBatis3FormattingUtilities
                    .getParameterClause(introspectedColumn));
            answer.addElement(new TextElement(sb.toString()));
        }

        if (context.getPlugins()
                .sqlMapDeleteByPrimaryKeyElementGenerated(answer,
                        introspectedTable)) {
            parentElement.addElement(answer);
        }
    }

    /**
     * 更新方法
     * @param parentElement
     */
    private void addUpdateByIdElements(XmlElement parentElement) {
        XmlElement answer = new XmlElement("update");

        answer
                .addAttribute(new Attribute(
                        "id", "updateById"));

        String parameterType;

        if (introspectedTable.getRules().generateRecordWithBLOBsClass()) {
            parameterType = introspectedTable.getRecordWithBLOBsType();
        } else {
            parameterType = introspectedTable.getBaseRecordType();
        }

        answer.addAttribute(new Attribute("parameterType", //$NON-NLS-1$
                parameterType));

        context.getCommentGenerator().addComment(answer);

        StringBuilder sb = new StringBuilder();

        sb.append("update "); //$NON-NLS-1$
        sb.append(introspectedTable.getFullyQualifiedTableNameAtRuntime());
        answer.addElement(new TextElement(sb.toString()));

        XmlElement dynamicElement = new XmlElement("set"); //$NON-NLS-1$
        answer.addElement(dynamicElement);

        for (IntrospectedColumn introspectedColumn : ListUtilities.removeGeneratedAlwaysColumns(introspectedTable
                .getNonPrimaryKeyColumns())) {
            XmlElement isNotNullElement = new XmlElement("if"); //$NON-NLS-1$
            sb.setLength(0);
            sb.append(introspectedColumn.getJavaProperty());
            sb.append(" != null"); //$NON-NLS-1$
            isNotNullElement.addAttribute(new Attribute("test", sb.toString())); //$NON-NLS-1$
            dynamicElement.addElement(isNotNullElement);

            sb.setLength(0);
            sb.append(MyBatis3FormattingUtilities
                    .getEscapedColumnName(introspectedColumn));
            sb.append(" = "); //$NON-NLS-1$
            sb.append(MyBatis3FormattingUtilities
                    .getParameterClause(introspectedColumn));
            sb.append(',');

            isNotNullElement.addElement(new TextElement(sb.toString()));
        }

        boolean and = false;
        for (IntrospectedColumn introspectedColumn : introspectedTable
                .getPrimaryKeyColumns()) {
            sb.setLength(0);
            if (and) {
                sb.append("  and "); //$NON-NLS-1$
            } else {
                sb.append("where "); //$NON-NLS-1$
                and = true;
            }

            sb.append(MyBatis3FormattingUtilities
                    .getEscapedColumnName(introspectedColumn));
            sb.append(" = "); //$NON-NLS-1$
            sb.append(MyBatis3FormattingUtilities
                    .getParameterClause(introspectedColumn));
            answer.addElement(new TextElement(sb.toString()));
        }

        if (context.getPlugins()
                .sqlMapUpdateByPrimaryKeySelectiveElementGenerated(answer,
                        introspectedTable)) {
            parentElement.addElement(answer);
        }
    }

    /**
     * 分页查询条件
     * @param parentElement
     */
    private void addPageWhereElements(XmlElement parentElement) {
        // 增加base_query
        XmlElement baseQuerySQL = new XmlElement("sql");

        baseQuerySQL.addAttribute(new Attribute("id", "page_where_clause"));
        //在这里添加where条件
        //设置trim标签
        XmlElement selectTrimElement = new XmlElement("trim");
        selectTrimElement.addAttribute(new Attribute("prefix", "WHERE"));
        //添加where和and
        selectTrimElement.addAttribute(new Attribute("prefixOverrides", "AND | OR"));
        StringBuilder sb = new StringBuilder();

        for(IntrospectedColumn introspectedColumn : introspectedTable.getAllColumns()) {

            String columnName = MyBatis3FormattingUtilities.getEscapedColumnName(introspectedColumn);
            if(columnIndexs == null || !columnIndexs.contains(columnName)){
                continue;
            }
            XmlElement selectNotNullElement = new XmlElement("if");
            sb.setLength(0);
            sb.append("null != ");
            sb.append(" queryObject.");
            sb.append(introspectedColumn.getJavaProperty());
            selectNotNullElement.addAttribute(new Attribute("test", sb.toString()));
            sb.setLength(0);
            // 添加and
            sb.append(" and ");
            sb.append(MyBatis3FormattingUtilities.getEscapedColumnName(introspectedColumn));
            // 添加等号
            sb.append(" = ");

            sb.append(getParameterClause(introspectedColumn,"queryObject."));
            selectNotNullElement.addElement(new TextElement(sb.toString()));
            selectTrimElement.addElement(selectNotNullElement);
        }
        baseQuerySQL.addElement(selectTrimElement);
        parentElement.addElement(baseQuerySQL);

    }

    public static String getParameterClause(
            IntrospectedColumn introspectedColumn, String prefix) {
        StringBuilder sb = new StringBuilder();

        sb.append("#{"); //$NON-NLS-1$
        sb.append(introspectedColumn.getJavaProperty(prefix));
        sb.append(",jdbcType="); //$NON-NLS-1$
        sb.append(introspectedColumn.getJdbcTypeName());

        if (stringHasValue(introspectedColumn.getTypeHandler())) {
            sb.append(",typeHandler="); //$NON-NLS-1$
            sb.append(introspectedColumn.getTypeHandler());
        }

        sb.append('}');

        return sb.toString();
    }

    /**
     * 查询总数
     * @param parentElement
     */
    private void addSelectCountElements(XmlElement parentElement) {
        // 公用select
        StringBuilder sb = new StringBuilder();
        sb.setLength(0);
        sb.append("select count(*)");
        sb.append(" from ");
        sb.append(introspectedTable.getFullyQualifiedTableNameAtRuntime());
        TextElement selectText = new TextElement(sb.toString());
        // 公用include
        XmlElement include = new XmlElement("include");
        include.addAttribute(new Attribute("refid", "page_where_clause"));

        // 增加list
        XmlElement list = new XmlElement("select");
        list.addAttribute(new Attribute("id", "count"));
        list.addAttribute(new Attribute("resultType", "int"));
        list.addAttribute(new Attribute("parameterType", introspectedTable.getBaseRecordType()));
        list.addElement(selectText);
        list.addElement(include);
        parentElement.addElement(list);
    }

    /**
     * 分页查询
     * @param parentElement
     */
    private void addPageListElements(XmlElement parentElement) {
        // 公用select
        StringBuilder sb = new StringBuilder();
        sb.setLength(0);
        sb.append("select ");

        TextElement selectText = new TextElement(sb.toString());
        // 公用include
        XmlElement include = new XmlElement("include");
        include.addAttribute(new Attribute("refid", "page_where_clause"));

        // 增加list
        XmlElement list = new XmlElement("select");
        list.addAttribute(new Attribute("id", "queryPageList"));
        list.addAttribute(new Attribute("resultMap", "BaseResultMap"));
        list.addElement(selectText);
        XmlElement baseColumnInclude = new XmlElement("include");
        baseColumnInclude.addAttribute(new Attribute("refid", "Base_Column_List"));
        list.addElement(baseColumnInclude);
        sb.setLength(0);
        sb.append("from ");
        sb.append(introspectedTable.getFullyQualifiedTableNameAtRuntime());
        TextElement tableText = new TextElement(sb.toString());
        list.addElement(tableText);

        list.addElement(include);
        sb.setLength(0);
        sb.append(" order by id desc ");
        TextElement orderByText = new TextElement(sb.toString());
        list.addElement(orderByText);

        XmlElement selectNotNullElement = new XmlElement("if");
        sb.setLength(0);
        sb.append(" page != null");
        selectNotNullElement.addAttribute(new Attribute("test", sb.toString()));
        sb.setLength(0);
        // 添加LIMIT
        sb.append(" LIMIT #{page.pageSize} OFFSET #{page.begin} ");
        selectNotNullElement.addElement(new TextElement(sb.toString()));
        list.addElement(selectNotNullElement);

        parentElement.addElement(list);
    }
}
